package com.viknix.threadpool.manager.cluster.factory;

import com.viknix.threadpool.manager.cluster.core.ServerMemberManager;
import com.viknix.threadpool.manager.cluster.lookup.FileConfigMemberLookup;
import com.viknix.threadpool.manager.cluster.lookup.MemberLookup;
import com.viknix.threadpool.manager.cluster.lookup.StandaloneMemberLookup;
import com.viknix.threadpool.manager.server.dependence.exception.ServerProcessException;
import com.viknix.threadpool.manager.server.dependence.util.EnvUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.Arrays;
import java.util.Objects;

/**
 * @Author: Dongqi
 * @Date: 2021/11/11 17:47
 * @Version 1.0
 * @Description: 一个寻址模式工厂，负责创建寻址模式。
 */
@Slf4j
public class LookupFactory {

    private static final String LOOKUP_MODE_TYPE = "tpm.core.member.lookup.type";

    /**
     * 当前寻址模式
     */
    @SuppressWarnings("checkstyle:StaticVariableName")
    private static MemberLookup LOOK_UP = null;

    /**
     * 当前寻址模式的枚举对象（单机模式下为null）
     */
    private static LookupType currentLookupType = null;

    /**
     * 创建目标寻址模式。
     *
     * @param memberManager {@link ServerMemberManager}
     * @return {@link MemberLookup}
     * @throws ServerProcessException serverProcessException
     */
    public static MemberLookup createLookUp(ServerMemberManager memberManager) throws ServerProcessException {

        // 判断是否是单机模式启动
        if (!EnvUtil.getStandaloneMode()) {
            String lookupType = EnvUtil.getProperty(LOOKUP_MODE_TYPE);
            LookupType type = chooseLookup(lookupType);
            // 构建寻址模式
            LOOK_UP = find(type);
            currentLookupType = type;
        } else {
            // 单机模式
            LOOK_UP = new StandaloneMemberLookup();
        }
        // 向 LOOK_UP 中注入 ServerMemberManager 属性。
        LOOK_UP.injectMemberManager(memberManager);
        log.info("Current addressing mode selection : {}", LOOK_UP.getClass().getSimpleName());
        return LOOK_UP;
    }

    /**
     * 切换到目标寻址模式。
     *
     * @param name          目标寻址模式的名称
     * @param memberManager {@link ServerMemberManager}
     * @return 目标寻址模式对象 {@link MemberLookup}
     * @throws ServerProcessException serverProcessException
     */
    public static MemberLookup switchLookup(String name, ServerMemberManager memberManager) throws ServerProcessException {

        // 按名称查找一个 枚举对象 LookupType，如果未找到，则返回 null。
        LookupType lookupType = LookupType.sourceOf(name);

        if (Objects.isNull(lookupType)) {
            throw new IllegalArgumentException(
                    "The addressing mode exists : " + name + ", just support : [" + Arrays.toString(LookupType.values())
                            + "]");
        }

        if (Objects.equals(currentLookupType, lookupType)) {
            return LOOK_UP;
        }
        // 如果当前的寻址模式和新的寻址模式不一样，切换新的寻址模式
        MemberLookup newLookup = find(lookupType);
        currentLookupType = lookupType;
        if (Objects.nonNull(LOOK_UP)) {
            LOOK_UP.destroy();
        }
        LOOK_UP = newLookup;
        // 向 LOOK_UP 中注入 ServerMemberManager 属性。
        LOOK_UP.injectMemberManager(memberManager);
        log.info("Current addressing mode selection : {}", LOOK_UP.getClass().getSimpleName());
        return LOOK_UP;
    }

    /**
     * 根据枚举类型返回 MemberLookup 对象
     *
     * @param type 枚举类型
     * @return MemberLookup 对象
     */
    private static MemberLookup find(LookupType type) {
        if (LookupType.FILE_CONFIG.equals(type)) {
            LOOK_UP = new FileConfigMemberLookup();
            return LOOK_UP;
        }
//        if (LookupType.ADDRESS_SERVER.equals(type)) {
//            LOOK_UP = new AddressServerMemberLookup();
//            return LOOK_UP;
//        }
        // unpossible to run here
        throw new IllegalArgumentException();
    }

    /**
     * 确定寻址模式枚举类型
     *
     * @param lookupType 配置的寻址模式（可能为空）
     * @return LookupType枚举类
     */
    private static LookupType chooseLookup(String lookupType) {
        // 如果已经配置了寻址模式，那么根据配置构建寻址模式
        if (StringUtils.isNotBlank(lookupType)) {
            LookupType type = LookupType.sourceOf(lookupType);
            if (Objects.nonNull(type)) {
                return type;
            }
        }
        // 判断 cluster.conf 文件是否存在
        File file = new File(EnvUtil.getClusterConfFilePath());
        if (file.exists() || StringUtils.isNotBlank(EnvUtil.getMemberList())) {
            // 如果存在使用 FileConfigMemberLookup
            return LookupType.FILE_CONFIG;
        }
        // 暂时不支持这种方式
        return LookupType.ADDRESS_SERVER;
    }

    public static MemberLookup getLookUp() {
        return LOOK_UP;
    }

    public static void destroy() throws ServerProcessException {
        Objects.requireNonNull(LOOK_UP).destroy();
    }

    public enum LookupType {

        /**
         * 文件寻址方式。
         */
        FILE_CONFIG(1, "file"),

        /**
         * 地址服务器寻址方式。
         */
        ADDRESS_SERVER(2, "address-server");

        private final int code;

        private final String name;

        LookupType(int code, String name) {
            this.code = code;
            this.name = name;
        }

        /**
         * 按名称查找一个 {@link LookupType}，如果未找到，则返回 null。
         *
         * @param name 需要查找的寻址模式名称
         * @return 该名称对应的枚举对象 {@link LookupType}
         */
        public static LookupType sourceOf(String name) {
            for (LookupType type : values()) {
                if (Objects.equals(type.name, name)) {
                    return type;
                }
            }
            return null;
        }

        public int getCode() {
            return code;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return name;
        }
    }
}
